package com.et.mserver;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Date;

public class MIOHandler extends IoHandlerAdapter {

	@Override
	public void messageReceived(final IoSession session, final Object message) throws Exception {

		MContext context = (MContext) session.getAttribute(MContext.SESSION_STATUS_KEY);
		if (context == null) {
			context = new MContext();
			session.setAttribute(MContext.SESSION_STATUS_KEY, context);
		}

		if (context.isInitState()) {

			this.handleForInitState(session, message);

			context.moveToBinaryReceivingState();

		} else if (context.isBinaryReceivingState()) {

			if (this.handleForBinayReceivingState(session, message, context)){
				// move to init state
				logger.info(Utility.logHeadOfSession(session) + "finish to receive binary, move to init state");
			} else {
				// do nothing
			}

		} else {
			throw new RuntimeException(Utility.logHeadOfSession(session) + "went to a bad state");
		}
	}

	private boolean handleForBinayReceivingState(final IoSession session, final Object message, MContext context) throws Exception {

		if (context == null) {
			throw new RuntimeException(Utility.logHeadOfSession(session) + "the context is null, when handler the binary receiving");
		}

		if (message instanceof BinaryMessageSegment) {

			final boolean handledLastBinarySegment = ((BinaryMessageSegment) message).isLastSegment();
			final byte[] bytes = ((BinaryMessageSegment) message).getBuffer();

			context.append(bytes, session);
			logger.info(Utility.logHeadOfSession(session) + "binary data length: {" + bytes.length + "} " + handledLastBinarySegment);

			final String bufferString = new String(bytes);
			logger.debug(Utility.logHeadOfSession(session) + "binary data: {" + bufferString + "} " + handledLastBinarySegment);

			final Date date = new Date();
			session.write(date);

			final long MAX_RECEIVING_BINARY_LENGTH = 32;
			if (context.getReceivingBinaryLength() >= MAX_RECEIVING_BINARY_LENGTH) {
				return true;
			} else {
				return false;
			}

		} else {

			throw new RuntimeException(Utility.logHeadOfSession(session) + "receive a bad message");
		}
	}

	private void handleForInitState(final IoSession session, final Object message) throws Exception {

		if (message instanceof Element) {

			final Element elem = (Element) message;
			logger.debug(Utility.logHeadOfSession(session) + "receive xml: {" + elem.getText() + "}");

		} else {

			final String str = message.toString();
			logger.debug(Utility.logHeadOfSession(session) + "receive text: {" + str + "}");

		}

		final Date date = new Date();
		session.write(date);

	}

	@Override
	public void sessionIdle(final IoSession session, final IdleStatus status) {

		if (session.isClosing()) {
			return;
		}

		final String clientIP = getClientIp(session);

		logger.info(Utility.logHeadOfSession(session) + "idleStauts " + status + " remoteIP " + clientIP);

		session.close(true);
	}

	private String getClientIp(final IoSession session) {

		final SocketAddress address = session.getRemoteAddress();
		final InetAddress ad = ((InetSocketAddress) address).getAddress();
		if (ad == null) {
			return null;
		}

		return ad.getHostAddress();
	}

	@Override
	public void exceptionCaught(final IoSession session, final Throwable cause) {

		logger.error(Utility.logHeadOfSession(session) + "exception: ", cause);
		session.close(true); //close session immediately
		logger.error(Utility.logHeadOfSession(session) + "close it for the exception");
	}

	@Override
	public void sessionClosed(final IoSession session) throws Exception {

		//socket断掉, 清除正在上传的文档记录, 为下一次上传做准备
		logger.info(Utility.logHeadOfSession(session) + "closed.");

		super.sessionClosed(session);
	}

	private static Logger logger = LoggerFactory.getLogger(MIOHandler.class);

}
